import logging
import traceback

from flask import request, jsonify, Response, stream_with_context
from flask.views import MethodView

from app.validators import mappings
from app.validators.items import items
from app.validators.mappings import IP_FIELDS, SN_FIELDS, MEASUREMENT_FIELDS, DECOUPLING_FIELDS, HZ_FIELDS, \
    COLLECT_FIELDS, PUBLIC_GET_FIELDS, BASE_R_FIELDS
from utils.enums import AllowRegisterTypeEnum
from utils.errors import ParamsError

logger = logging.getLogger(__name__)


class BaseJsonView(MethodView):
    req = None
    valid_type = []

    def post(self):
        try:
            req = request.json if request.data else {}
            return self._process(req)
        except ParamsError as e:
            logger.warning(f'req lost body, return')
            return self.response(-1, e.message, False)
        except Exception as e:
            logger.error(f"system error, msg: {traceback.format_exc()}")
            return self.response(-1, e.args[-1], False)

    def _process(self, req):
        """
        请求处理入口
        :return:
        """
        raise NotImplemented('func not implement')

    def _public_process(self, *, func_name, req):
        try:
            success, msg, data = getattr(items, func_name)(data=req)
        except Exception:
            logger.error(f'get register value error, msg: {traceback.format_exc()}')
            return self.response(-1, msg='error')
        code = 0 if success else -1
        return self.response(code, msg=msg, data=data)

    @staticmethod
    def _check_params(*, data: dict, mapping):
        results = list(filter(lambda x: x not in data or not data[x], mapping))
        if results:
            raise ParamsError(message=f'字段: {", ".join(results)} 不允许为空')

    @staticmethod
    def _get_register_mapping(register):
        name = f'{AllowRegisterTypeEnum(register).name}_FIELDS'
        mapping_fields = getattr(mappings, name)
        return mapping_fields

    @staticmethod
    def response(result=0, msg='success', success=True, data=None, **kwargs):
        resp = dict(result=result, msg=msg)
        if data:
            resp['data'] = data
        if kwargs:
            resp.update(kwargs)
        return jsonify(resp)


class GetDeviceView(BaseJsonView):

    def _process(self, req):
        results = items.get_devices()
        return self.response(data=results)


class GetDeviceMView(BaseJsonView):

    def _process(self, req):
        mapping = PUBLIC_GET_FIELDS
        self._check_params(data=req, mapping=mapping)
        func = '_get_config_measurement'
        return self._public_process(func_name=func, req=req)


class GetDeviceDeView(BaseJsonView):

    def _process(self, req):
        mapping = PUBLIC_GET_FIELDS
        self._check_params(data=req, mapping=mapping)
        func = '_get_config_decoupling'
        return self._public_process(func_name=func, req=req)


class GetDeviceHzView(BaseJsonView):

    def _process(self, req):
        mapping = PUBLIC_GET_FIELDS
        self._check_params(data=req, mapping=mapping)
        func = '_get_config_hz'
        return self._public_process(func_name=func, req=req)


class ConfigRegisterIpView(BaseJsonView):

    def _process(self, req):
        mapping = IP_FIELDS
        self._check_params(data=req, mapping=mapping)
        func = '_set_config_ip'
        return self._public_process(func_name=func, req=req)


class ConfigRegisterSnView(BaseJsonView):

    def _process(self, req):
        mapping = SN_FIELDS
        self._check_params(data=req, mapping=mapping)
        func = '_set_config_sn'
        return self._public_process(func_name=func, req=req)


class ConfigRegisterMView(BaseJsonView):
    """
    量程
    """

    def _process(self, req):
        mapping = MEASUREMENT_FIELDS
        self._check_params(data=req, mapping=mapping)
        func = '_set_config_measurement'
        return self._public_process(func_name=func, req=req)


class ConfigRegisterHzView(BaseJsonView):

    def _process(self, req):
        mapping = HZ_FIELDS
        self._check_params(data=req, mapping=mapping)
        func = '_set_config_hz'
        return self._public_process(func_name=func, req=req)


class ConfigRegisterDeView(BaseJsonView):

    def _process(self, req):
        mapping = DECOUPLING_FIELDS
        self._check_params(data=req, mapping=mapping)
        func = '_set_config_decoupling'
        return self._public_process(func_name=func, req=req)


class ControlRegisterStartView(BaseJsonView):

    def _process(self, req):
        mapping = COLLECT_FIELDS
        self._check_params(data=req, mapping=mapping)
        func = '_set_control_collect'
        body = dict(sn=req['sn'], signal=1)
        return self._public_process(func_name=func, req=body)


class ControlRegisterEndView(BaseJsonView):

    def _process(self, req):
        mapping = COLLECT_FIELDS
        self._check_params(data=req, mapping=mapping)
        func = '_set_control_collect'
        body = dict(sn=req['sn'], signal=2)
        return self._public_process(func_name=func, req=body)


class ControlRegisterZeroView(BaseJsonView):

    def _process(self, req):
        mapping = COLLECT_FIELDS
        self._check_params(data=req, mapping=mapping)
        func = '_set_control_collect'
        body = dict(sn=req['sn'], signal=3)
        return self._public_process(func_name=func, req=body)


class GetDeviceUploadDataView(BaseJsonView):

    def _process(self, req):
        mapping = BASE_R_FIELDS
        self._check_params(data=req, mapping=mapping)
        func = '_get_device_data'
        body = dict(sn=req['sn'])
        return self._public_process(func_name=func, req=body)


class ExportDeviceDataView(BaseJsonView):

    def _process(self, req):
        mapping = BASE_R_FIELDS
        self._check_params(data=req, mapping=mapping)
        func = '_export_device_data'
        body = dict(sn=req['sn'])
        success, file_path, name = getattr(items, func)(data=body)
        if not success:
            return self.response(-1, msg=f'设备: {req["sn"]} 未采集')

        response = Response(stream_with_context(open(file_path, 'rb')), mimetype='text/csv')
        response.headers.set("Content-Disposition", "attachment", filename=f"{name}")
        return response
